{
  * Debug Info
  *
  * Currently Dwarf only (based on fpdwarf)
}
unit vdbg.dbginfo;

interface

{$mode delphi}

uses
  LCLProc,
  SysUtils,

  Debugger,

  FpDbgDwarf,
  FpDbgLoader,
  FpDbgInfo;

type

  { TVDbgInfo }

  TVDbgInfo = class
  private
    FDwarf: TDbgDwarf;
  public
    constructor Create;
    destructor Destroy; override;

    // Should be called on each module loaded to load symbols for this module
    // from file.
    procedure AddSymbolsFromFile(const AFileName: string);

    // Find address in memory for specific line number in file pointed by
    // file name.
    function GetLineAddress(const AFileName: string; ALine: integer): TDbgPtr;

    function FindSymbol(AAddress: TDbgPtr): TDbgSymbol;
  end;

implementation

{ TVDbgInfo }

constructor TVDbgInfo.Create;
begin
  inherited Create;
end;

destructor TVDbgInfo.Destroy;
begin
  FDwarf.Free;
  inherited Destroy;
end;

procedure DumpCompilationUnit(const cu: TDwarfCompilationUnit);
var
  map: PDWarfLineMap;
  {$IFDEF DUMP_DWARF_LINE_ADDRESS}
  i: integer;
  addr: TDBGPtr;
  {$ENDIF}
begin
  DebugLn('-------------------------------------');
  DebugLn('[cu] unit name: "%s"', [cu.UnitName]);
  DebugLn('[cu] file name: "%s"', [cu.FileName]);
  DebugLn('[cu] valid: %s', [BoolToStr(cu.Valid, True)]);

  // Get line-address map. At least it calls BuildLineInfo, so GetLineAddress
  // can be called later without Access Violation.
  // todo: This must be solved properly later.
  map := cu.GetLineAddressMap(cu.FileName);

  {$IFDEF DUMP_DWARF_LINE_ADDRESS}
  if Assigned(map) then
  begin
    for i := 0 to map.AddressCount - 1 do
    begin
      addr := map.Address[i];
      if addr <> 0 then
        DebugLn('  line: %-5d addr: %x', [i, addr]);
    end;
  end;
  {$ENDIF}
  DebugLn('-------------------------------------');
end;

procedure TVDbgInfo.AddSymbolsFromFile(const AFileName: string);
var
  FLoader: TDbgImageLoader;
  i, cuCnt: integer;
begin
  DebugLn('[vdbg] Trying to load debug info from "%s"', [AFileName]);

  if not FileExists(AFileName) then
     Exit;

  FLoader := TDbgImageLoader.Create(AFileName);
  try
    FDwarf := TDbgDwarf.Create(FLoader);
    cuCnt := FDwarf.LoadCompilationUnits;


    for i := 0 to cuCnt - 1 do
    begin
      //DumpCompilationUnit(FDwarf.CompilationUnits[i]);

      // Get compilation unit built? To allow GetLineAddress later without AV.
      // todo: fix it.
      FDwarf.CompilationUnits[i].GetLineAddressMap(AFileName);
    end;

    DebugLn('[vdbg] FDwarf.LoadCompilationUnits done (%d comp. unit(s))', [cuCnt]);
  finally
    FLoader.Free;
  end;
end;

function TVDbgInfo.GetLineAddress(const AFileName: string; ALine: integer
  ): TDbgPtr;
begin
  try
    Result := FDwarf.GetLineAddress(AFileName, ALine);
  except on e: Exception do
    DebugLn(E.ToString);
  end;
end;

function TVDbgInfo.FindSymbol(AAddress: TDbgPtr): TDbgSymbol;
begin
  Result := nil;
  try
    Result := FDwarf.FindSymbol(AAddress);
  except on e: Exception do
    DebugLn(E.ToString);
  end;
end;

end.
